//------------------------------------------------------------------------------ 
//      Copyright (c) 2004-2009, Be Nanashi-San 
// 
//      Permission is hereby granted, free of charge, to any person obtaining a 
//      copy of this software and associated documentation files (the "Software"), 
//      to deal in the Software without restriction, including without limitation 
//      the rights to use, copy, modify, merge, publish, distribute, sublicense, 
//      and/or sell copies of the Software, and to permit persons to whom the 
//      Software is furnished to do so, subject to the following conditions: 
// 
//      The above copyright notice and this permission notice shall be included in 
//      all copies or substantial portions of the Software. 
// 
//      THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
//      IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
//      FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
//      AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
//      LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
//      FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
//      DEALINGS IN THE SOFTWARE. 
// 
//      File Name:   DisplayThreadView.cpp
//      Author:      Be Nanashi-San 
//      Description: 	 
//------------------------------------------------------------------------------

//#define DEBUG

#include <Alert.h>
#include <Beep.h>
#include <Debug.h> // for PRINT()
#include <Directory.h>
#include <File.h>
#include <Message.h>
#include <ScrollView.h>
#include <Path.h>
#include <UTF8.h>
#include <LayoutBuilder.h>
#include <GroupView.h>

#include "2chPositive.h"
#include "DisplayThreadView.h"
#include "NetUtil.h"
#include "Util.h"
#include "datattr.h"
#include "messages.h"
#include "setting.h"
#include "BBSHandler.h"
#include "mainWindow.h"
#include "K2chTextView.h"
////////////////////////////////////////////////////////////////////////////////
// DisplayThreadView
//
//
////////////////////////////////////////////////////////////////////////////////
DisplayThreadView::DisplayThreadView()
:BGroupView()
,fText(NULL)
//,fCurrentMessage(NULL)
{
	this->SetViewColor(B_TRANSPARENT_32_BIT);
	fText = new K2chTextView("2ch:TextView");
	BScrollView *aScrollView = new BScrollView("Display Thread"
						, fText
						, B_FOLLOW_ALL, false, true);

	BLayoutBuilder::Group<>(this)
			.AddGroup(B_VERTICAL, 0.0f)
			.Add(aScrollView)
			.End();
}
////////////////////////////////////////////////////////////////////////////////
// ~DisplayThreadView
//
//
////////////////////////////////////////////////////////////////////////////////
DisplayThreadView::~DisplayThreadView()
{
//	delete fCurrentMessage;
}
////////////////////////////////////////////////////////////////////////////////
// MessageReceived
//
//
////////////////////////////////////////////////////////////////////////////////
void
DisplayThreadView::MessageReceived (BMessage * message)
{
	switch (message->what){
		case kMSG_OPEN_THREAD:{
		//zeta 2 スレ@624のBe名無しさんより。再読込み用に保存。
			//delete fCurrentMessage; //20050626追加。メモリ漏れ?
			//fCurrentMessage = new BMessage(*message);
			fCurrentMessage = *message;
			fCurrentEOF = 0;
			this->DisplayThread(theSettings().GetBool(kSETTING_OFFLINE));
			break;
		}

		case kMSG_FIND:	{
			if (!fText) break;
			BMessenger(fText).SendMessage(message);
			break;
		}

		case kMSG_JUMP:{
			int32 resno;
			message->FindInt32("jump", &resno);
			this->JumpToRes(resno);
			break;
		}
		
		case kMENU_THREAD_OPEN_WEBBROWER:
			this->OpenWithBrowser();
			break;

		//zeta 2 スレ624のBe名無しさんより。
		case kMENU_THREAD_REFREASH:
			if (!fCurrentMessage.IsEmpty())
				this->DisplayThread(false, false);
			break;

		case kMEG_WRITE_BUTTON:
			if (fCurrentMessage.IsEmpty()) break;
			message->AddMessage("sure", &fCurrentMessage);
			be_app_messenger.SendMessage(message);
			break;
			
		case kUPDATEPREFS:
			if (message->HasString(DISPLAY_RES_FONT_NAME) ||
					message->HasString(DISPLAY_RES_FONT_SIZE))
					fText->UpdatePreferences(message);
			break;

		default:
			BView::MessageReceived (message);
			break;
	}
}
////////////////////////////////////////////////////////////////////////////////
// LoadLocalDat
// ローカルキャッシュを開く。
// 2005.6.26:引数displayMsg削除
////////////////////////////////////////////////////////////////////////////////
status_t
DisplayThreadView::LoadLocalDat(BFile &file)
{
const char *uri, *datname;

	fCurrentMessage.FindString("dat", &datname);
	fCurrentMessage.FindString("host", &uri);
	BDirectory dir;

	findcachedir(&dir, kCACHE_DIR_NAME, uri);
	//B_READ_WRITEなのは、差分追加に備えてのため。
	return file.SetTo(&dir, datname, B_READ_WRITE);
}
/////////////////////////////////////////////////////////////////////////////////
// void catdat(BFile &dat, BMallocIO &delta)
// datファイルと差分を連結。
/////////////////////////////////////////////////////////////////////////////////
inline void
catdat(BFile &dat, BMallocIO &delta)
{
//20060520:datの最後の改行を削除
	dat.Seek(-1, SEEK_END);
	dat.Write(delta.Buffer(), delta.BufferLength());

}
////////////////////////////////////////////////////////////////////////////////
// LoadDatFromINet
// ネットよりdatファイルを取得。
// 2005.6.26:引数displayMsg削除
////////////////////////////////////////////////////////////////////////////////
status_t
DisplayThreadView::LoadDatFromINet(BFile &file )
{
	http_info hinfo;
	hinfo.init(false);
	
	status_t sts;
	sts = this->LoadLocalDat(file);
	if(sts == B_OK){
		file.ReadAttrString("Last Modified", &hinfo.modified);
		off_t n;
		file.GetSize(&n);
		hinfo.body_length = n;
	}

	BString uri, datname;
	fCurrentMessage.FindString("host", &uri);
	fCurrentMessage.FindString("dat", &datname);
	char host[64], subdir[64], filename[64];
	uri2host(uri.String(), host, subdir);
	
	BMallocIO outbuf;
	outbuf.SetBlockSize(1024 * 4); //20060803
	
	if(datname.FindFirst("cgi") != B_ERROR){
	//matchbbs
	//TODO:サブクラス分け。
		BString keyname(datname);
		keyname.RemoveFirst(".cgi");
		sprintf(filename, "bbs/offlaw.cgi/%s/%s", subdir, keyname.String());
//		sprintf(filename, "bbs/read.cgi/%s/%s", subdir, keyname.String());
//		hinfo.res_start = 1;
		hinfo.modified = "";
		hinfo.gzipped=false;
		sts = matchbbsthreadget(host, filename, outbuf, hinfo);
	}else{
		sprintf(filename, "%s/dat/%s", subdir, datname.String());
		sts = get(host, filename, outbuf, hinfo);
	}

	if (sts == B_ERROR) return B_ERROR;
	// 変更無し。
	if (sts == HTTP_NOT_MODIFIED) return HTTP_NOT_MODIFIED;
	 
	// 全てを再取得。
	// -HTTP_PARTIAL_CONTENT 新着あり + あぼ～んあり。
	// HTTP_RANGE_NOT_SATISFIABLE 新着なし + あぼ～んあり。20090312
	if (sts == -HTTP_PARTIAL_CONTENT || sts == HTTP_RANGE_NOT_SATISFIABLE) {
		hinfo.init(true);
		sts = get(host, filename, outbuf, hinfo);
		if (sts == B_ERROR) return B_ERROR;
	}

	//差分あり
	if(sts == HTTP_PARTIAL_CONTENT){
		catdat(file, outbuf);
		file.WriteAttrString("Last Modified", &hinfo.modified);
	}else{
	//新規dat作成。
		BString title;
		BDirectory dir;
		findcachedir(&dir, kCACHE_DIR_NAME, uri.String());
		fCurrentMessage.FindString("title", &title);
		dir.CreateFile(datname.String(), &file);
		file.Write(outbuf.Buffer(), outbuf.BufferLength());
		file.WriteAttrString("Last Modified", &hinfo.modified);
		file.WriteAttrString(DAT_ATTR_TITLE, &title);
		file.WriteAttrString(DAT_ATTR_URI, &uri);
		file.WriteAttrString(DAT_ATTR_DATNAME, &datname);
	}	

	return B_OK;
}
////////////////////////////////////////////////////////////////////////////////
// DisplayThread
// スレッド表示。
// 2005.6.26:引数displayMsg削除
////////////////////////////////////////////////////////////////////////////////
void
DisplayThreadView::DisplayThread(bool uselocal, bool sendHistory)
{
BFile file;// = new BFile;
status_t sts = B_OK;

	//20060916
	if(!strcmp((static_cast<a2chViewer *>(be_app)->CurReader())->GetName(), "ローカルデータ")){
		if( this->LoadLocalDat(file) != B_OK){
			(new BAlert("","オフラインでエラーが発生しました","OK"))->Go();
			return;
		}
	}else{
		if(uselocal){
			if( this->LoadLocalDat(file) != B_OK){
				(new BAlert("","オフラインでエラーが発生しました","OK","オンラインにする"))->Go();
				sts = this->LoadDatFromINet(file);
			}
		}else{
			if( (sts = this->LoadDatFromINet(file)) == HTTP_NOT_MODIFIED)
				sts = this->LoadLocalDat(file);
		}
		//2005/05/03
		if(sts == B_ERROR){
			(new BAlert("","サーバーに接続できません", "OK"))->Go();
			//delete file; //20071203 memory leak?
			return;
		}
	}
	
	if (sendHistory)	this->SendHistory();
		
	//スレ順書き込み。
	int32 no;
	fCurrentMessage.FindInt32("no", &no);
	file.WriteAttr(DAT_ATTR_THREADNO, B_INT32_TYPE, 0, &no, sizeof(int32));
	
	//zeta 2 スレ@713のBe名無しさんより修正。
	//20051116:Reloadのチェック追加。（サイズが同じならReloadと見なさない）
	//TODO:onlineのときはget()で新着なし確認必要？（あぼーんもあるのでサイズ変わるかも？）
	off_t size;
	file.GetSize(&size);
//	fText->LoadFile(file, (fCurrentEOF != 0) );
	fText->LoadFile(file);
	fCurrentEOF = size;

	BString title;
	fCurrentMessage.FindString("title", &title);
	BView::TruncateString(&title, B_TRUNCATE_MIDDLE, 500);	
	this->Window()->SetTitle(title.String());
	BView::TruncateString(&title, B_TRUNCATE_END, 100);	
	this->SetName(title.String());
//	(this->Parent())->DrawTabs();
}
////////////////////////////////////////////////////////////////////////////////
// SendHistory
// 
//
////////////////////////////////////////////////////////////////////////////////
void
DisplayThreadView::SendHistory()
{
	BMessage msg(kMSG_BOOKMARK);
	BString s;
	fCurrentMessage.FindString("title", &s);
	BView::TruncateString(&s, B_TRUNCATE_MIDDLE, 300);	
	msg.AddString("title", s);
	fCurrentMessage.FindString("url", &s);
	msg.AddString("url", s);
	((mainWindow *)Window())->AddHistory(msg);
}

////////////////////////////////////////////////////////////////////////////////
// JumpToRes
// 指定レス番にジャンプ。
//
////////////////////////////////////////////////////////////////////////////////
void
DisplayThreadView::JumpToRes(int32 resno)
{
	PRINT(("JumpToRes(%ld)\n", resno));
	if(fText)	fText->ScrollToRes(resno); 
}
////////////////////////////////////////////////////////////////////////////////
// OpenWithBrowser
// Browserでスレをオープン。
//
////////////////////////////////////////////////////////////////////////////////
void
DisplayThreadView::OpenWithBrowser()
{
	if (fCurrentMessage.IsEmpty())	return;
	
// TODO: 2ch以外の処理
	BString uri, datname;
	fCurrentMessage.FindString("host", &uri);
	fCurrentMessage.FindString("dat", &datname);
	datname.RemoveAll(".dat");
	char host[64], subdir[64];
	uri2host(uri.String(), host, subdir);
	BString u2;
	u2 << "http://" << host <<"/test/read.cgi/" << subdir << "/" << datname;
	
	Launcher(B_URL_HTTP, u2.String());
}
////////////////////////////////////////////////////////////////////////////////
// Text
////////////////////////////////////////////////////////////////////////////////
K2chTextView*
DisplayThreadView::Text()
{
	return fText;
}
